package main

import (
	"fmt"
	"io"
	"net"
	"os"
	"sync"
	"sync/atomic"
)

func main() {
	// 死锁
	//deadLock()

	//如果 f 方法执行的时候 panic，或者 f 执行初始化资源的时候失败了，这个时候，Once 还是会认为初次执行已经成功了，即使再次调用 Do 方法，也不会再次执行 f。
	// 【【【使用自定义的Once结构体，可以解决这个问题。】】】
	//notInit()

	testMyOnce()
}

func notInit() {
	var once sync.Once
	var googleConn net.Conn // 到Google网站的一个连接

	once.Do(func() {
		// 建立到google.com的连接，有可能因为网络的原因，googleConn并没有建立成功，此时它的值为nil
		googleConn, _ = net.Dial("tcp", "google.com:80")
	})
	// 发送http请求
	googleConn.Write([]byte("GET / HTTP/1.1\r\nHost: google.com\r\n Accept: */*\r\n\r\n"))
	io.Copy(os.Stdout, googleConn)
}
func deadLock() {
	var once sync.Once
	once.Do(func() {
		once.Do(func() {
			fmt.Println("初始化")
		})
	})
}

// Once 一个功能更加强大的Once
type Once struct {
	m    sync.Mutex
	done uint32
}

// Do 传入的函数f有返回值error，如果初始化失败，需要返回失败的error
// Do方法会把这个error返回给调用者
func (o *Once) Do(f func() error) error {
	if atomic.LoadUint32(&o.done) == 1 { //fast path
		return nil
	}
	return o.slowDo(f)
}

// 如果还没有初始化
func (o *Once) slowDo(f func() error) error {
	o.m.Lock()
	defer o.m.Unlock()
	var err error
	if o.done == 0 { // 双检查，还没有初始化
		err = f()       // 执行函数
		if err == nil { // 初始化成功才将标记置为已初始化
			atomic.StoreUint32(&o.done, 1)
		}
	}
	return err
}

func testMyOnce() {
	var once Once
	//一个函数
	f := func() error {
		googleConn, err := net.Dial("tcp", "google.com:80")
		if err != nil {
			return err
		}
		fmt.Println(googleConn)
		return nil
	}

	err := once.Do(f)
	if err != nil {
		fmt.Println(err.Error())
	}
}
